package com.liu.stockmarket.utils

import android.app.Activity
import android.app.UiModeManager
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Environment
import android.text.Editable
import android.text.TextWatcher
import android.view.View
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import com.github.mikephil.charting.charts.Chart
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.highlight.Highlight
import com.github.mikephil.charting.listener.OnChartValueSelectedListener
import com.google.android.material.tabs.TabLayout
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.liu.stockmarket.R
import com.liu.stockmarket.gson.LocalDateAdapter
import com.liu.stockmarket.gson.LocalDateTimeAdapter
import com.liu.stockmarket.model.*
import com.liu.stockmarket.repository.database.entity.StockEntity
import com.liu.stockmarket.ui.stock.HoldingStockAdapter
import com.lxj.xpopup.XPopup
import com.lxj.xpopup.core.BasePopupView
import com.lxj.xpopup.interfaces.OnCancelListener
import com.lxj.xpopup.interfaces.OnConfirmListener
import com.lxj.xpopup.interfaces.OnSelectListener
import com.lxj.xpopupext.listener.TimePickerListener
import com.lxj.xpopupext.popup.TimePickerPopup
import java.io.File
import java.io.FileNotFoundException
import java.lang.StringBuilder
import java.math.BigDecimal
import java.math.RoundingMode
import java.time.*
import java.time.format.DateTimeFormatter
import java.util.*

const val YMDHM = "yyyy-MM-dd HH:mm"
const val YMDHMS = "yyyy-MM-dd HH:mm:ss"
const val YMD = "yyyy-MM-dd"
const val YM = "yyyy-MM"
const val MD = "MM-dd"
const val YYYYMMDD = "yyyyMMdd"


val YMD_FORMAT: DateTimeFormatter = DateTimeFormatter.ofPattern(YMD)
val YMDHM_FORMAT: DateTimeFormatter = DateTimeFormatter.ofPattern(YMDHM)
val YMDHMS_FORMAT: DateTimeFormatter = DateTimeFormatter.ofPattern(YMDHMS)
val YM_FORMAT: DateTimeFormatter = DateTimeFormatter.ofPattern(YM)
val MD_FORMAT: DateTimeFormatter = DateTimeFormatter.ofPattern(MD)
val YYYYMMDD_FORMAT: DateTimeFormatter = DateTimeFormatter.ofPattern(YYYYMMDD)

val DAY_OF_WEEK = mapOf(
    1 to "周一",
    2 to "周二",
    3 to "周三",
    4 to "周四",
    5 to "周五",
    6 to "周六",
    7 to "周日",
)


fun LocalDate.YMD() = YMD_FORMAT.format(this)
fun LocalDate.YYYYMMDD() = YYYYMMDD_FORMAT.format(this)
fun LocalDate.YM() = YM_FORMAT.format(this)
fun LocalDateTime.YM(): String {
    return this.toLocalDate().YM()
}

fun LocalDateTime.MD(): String {
    return MD_FORMAT.format(this)
}

fun LocalDateTime.YMD(): String {
    return this.toLocalDate().YMD()
}

fun LocalDateTime.YMDHM(): String {
    return YMDHM_FORMAT.format(this)
}

fun LocalDateTime.YMDHMS(): String {
    return YMDHMS_FORMAT.format(this)
}


fun String.YMDParse() = LocalDate.parse(this, YMD_FORMAT)
fun String.YMDHMParse() = LocalDateTime.parse(this, YMDHM_FORMAT)
fun String.YMDHMSParse() = LocalDateTime.parse(this, YMDHMS_FORMAT)

fun EditText.getString() = this.text.toString().trim()
fun TextView.getString() = this.text.toString().trim()

/**
 * 获得周几
 * */
fun LocalDate.week(): String {
    return LocalDateTime.of(this, LocalTime.MAX).week()
}

fun LocalDateTime.week(): String {
    val dayOfWeek = this.dayOfWeek.value
    return DAY_OF_WEEK.getOrDefault(dayOfWeek, "")
}

fun Context.toast(message: String, duration: Int = Toast.LENGTH_LONG) {
    Toast.makeText(this, message, duration).show()
}

fun Activity.isNightMode(): Boolean {
    val uiModeManager =
        this.getSystemService(Context.UI_MODE_SERVICE) as UiModeManager
    return uiModeManager.nightMode ==MODE_NIGHT_YES
}

/**
 * 时间选择款，默认添加当前的时间信息
 */
fun Context.selectDate(mode: TimePickerPopup.Mode, onTimeConfirm: (date: LocalDateTime) -> Unit) {
    val timePickerPopup = TimePickerPopup(this)
        .setMode(mode)
        .setTimePickerListener(object : TimePickerListener {
            override fun onTimeChanged(date: Date?) {
            }

            override fun onTimeConfirm(date: Date?, view: View?) {
                date?.let {
                    val toLocalDate =
                        date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()
                    onTimeConfirm(toLocalDate)
                }
            }
        })

    XPopup.Builder(this)
        .isDarkTheme(AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES)
        .asCustom(timePickerPopup)
        .show()
}

fun Context.xPopupDialog(popupView: BasePopupView): BasePopupView {
    return XPopup.Builder(this)
        .isDarkTheme(AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES)
        .asCustom(popupView)
        .show()
}


fun Context.confirm(
    title: String,
    content: String,
    okListener: OnConfirmListener,
    cancelListener: OnCancelListener? = null
) {
    val uiModeManager =
        this.getSystemService(Context.UI_MODE_SERVICE) as UiModeManager
    XPopup.Builder(this)
        .isDarkTheme(uiModeManager.nightMode == 2)
        .asConfirm(title, content, okListener, cancelListener)
        .show()
}

fun Context.bottomListPopup(title: String, list: List<String>, selectedListener: OnSelectListener) {
    val uiModeManager =
        this.getSystemService(Context.UI_MODE_SERVICE) as UiModeManager
    XPopup.Builder(this)
        .isDarkTheme(uiModeManager.nightMode == 2)
        .asBottomList(title, list.toTypedArray(), selectedListener)
        .show()
}

inline fun <reified T : Activity> Activity.startActivity(bundle: Bundle? = null) {
    val intent = Intent(this, T::class.java)
    if (bundle != null) {
        intent.putExtras(bundle)
    }
    startActivity(intent)
}

fun Context.start(bundle: Bundle, cls: Class<out Any>) {
    val intent = Intent(this, cls)
    intent.putExtras(
        bundle
    )
    this.startActivity(intent)
}

fun BigDecimal.getString(scale: Int = 2, roundingMode: Int = BigDecimal.ROUND_UP): String {
    return this.setScale(scale, roundingMode).stripTrailingZeros().toPlainString()
}

fun TextView.textColor(context: Context, bigDecimal: BigDecimal) {
    when {
        bigDecimal > BigDecimal.ZERO -> {
            this.setTextColor(context.getColor(R.color.up))
        }
        bigDecimal < BigDecimal.ZERO -> {
            this.setTextColor(context.getColor(R.color.down))
        }
        else -> {
            this.setTextColor(context.getColor(R.color.black))
        }
    }
}

fun TextView.money(context: Context, bigDecimal: BigDecimal) {
    this.textColor(context,bigDecimal)
    this.text = bigDecimal.getString()
}

fun TextView.percent(context: Context, bigDecimal: BigDecimal) {
   this.textColor(context, bigDecimal)
    this.text = bigDecimal.getString(3, BigDecimal.ROUND_HALF_UP) + "%"
}

fun TextView.percentBg(context: Context, bigDecimal: BigDecimal) {
    when {
        bigDecimal > BigDecimal.ZERO -> {
            this.setBackgroundColor(context.getColor(R.color.up))
        }
        bigDecimal < BigDecimal.ZERO -> {
            this.setBackgroundColor(context.getColor(R.color.down))
        }
        else -> {
            // 不亏就是赚
            this.setBackgroundColor(context.getColor(R.color.up))
        }
    }
    this.text = bigDecimal.getString(3, BigDecimal.ROUND_HALF_UP) + "%"
}

fun EditText.afterTextChanged(afterTextChanged: (String) -> Unit) {
    this.addTextChangedListener(object : TextWatcher {
        override fun afterTextChanged(editable: Editable?) {
            afterTextChanged.invoke(editable.toString())
        }

        override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}

        override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
    })
}

fun TabLayout.onTabSelected(onTableSelected: (TabLayout.Tab) -> Unit) {
    this.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
        override fun onTabSelected(tab: TabLayout.Tab) {
            onTableSelected.invoke(tab)

        }

        override fun onTabUnselected(tab: TabLayout.Tab?) {
        }

        override fun onTabReselected(tab: TabLayout.Tab?) {
        }
    })
}

/**
 * 是否是新债，未上市
 */
fun isNewBond(transactionInfo: HoldingStockProfit.StockTransactionInfo): Boolean {
    return (transactionInfo.yesterdayPrice.setScale(5) == BigDecimal("100").setScale(5)
            && transactionInfo.nowPrice.setScale(5) == BigDecimal.ZERO.setScale(5))


}

fun isNewBond(diff: Diff): Boolean {
    return (diff.f18.setScale(5) == BigDecimal("100").setScale(5)
            && diff.f2.setScale(5) == BigDecimal.ZERO.setScale(5))

}


fun Chart<*>.onChartValueSelectedListener(
    onValueSelected: ((e: Entry, h: Highlight) -> Unit),
    onNothingSelected: (() -> Unit)? = null
) {
    this.setOnChartValueSelectedListener(object : OnChartValueSelectedListener {
        override fun onValueSelected(e: Entry, h: Highlight) {
            onValueSelected.invoke(e, h)
        }

        override fun onNothingSelected() {
            onNothingSelected?.invoke()
        }
    })
}


/**
 * 按position 选中
 */
fun TabLayout.selectByPosition(position: Int) {
    this.selectTab(this.getTabAt(position))


}


fun Float.toString(scale: Int): String {
    return BigDecimal(this.toDouble()).setScale(scale, RoundingMode.HALF_UP).toString()
}

fun String?.isBigDecimal(): Boolean {
    if (this == null) {
        return false
    }
    return try {

        BigDecimal(this)
        true
    } catch (e: NumberFormatException) {
        false
    }
}

fun ResponseResult<*>.isSuccess(): Boolean {
    return this.code == ResponseResult.SUCCESS
}

/**
 * 如 0.127043,1.601012,1.113048,0.002176,1.113051,1.118002,1.110081,1.113626,0.127040,0.128093,0.127029,1.601398,0.123120,1.000001
 */
fun List<StockEntity>.toStockSecids(): String {
    return this.joinToString(separator = ",") {
        it.transactionExchange.getCode().toString() + "." + it.code
    }
}

fun StockEntity.toStockSecids(): String {
    return this.transactionExchange.getCode().toString() + "." + this.code
}

/**
 * 127043,601012
 */
fun List<StockEntity>.toFundCodes(): String {
    return this.joinToString(separator = ",") {
        it.code
    }
}


/**
 * 基金转换
 */
fun List<FundTransactionInfo>.toStockTransactionInfo(): List<HoldingStockProfit.StockTransactionInfo> {
    return this.map {


        // 收盘c
        if (it.PDATE != "--" && it.PDATE == it.GZTIME.substring(0, 10)) {
            // 准确
            val upOrDownMoney =
                it.NAV.setScale(5) - (it.NAV.setScale(5) / (BigDecimal("1") + it.NAVCHGRT * BigDecimal(
                    "0.01"
                )).setScale(5))

            HoldingStockProfit.StockTransactionInfo(
                it.FCODE,
                it.SHORTNAME,
                it.NAV,
                it.NAVCHGRT,
                upOrDownMoney.setScale(5),
                it.NAV
            )
        } else {
            // 估算
            HoldingStockProfit.StockTransactionInfo(
                it.FCODE,
                it.SHORTNAME,
                it.GSZ,
                it.GSZZL,
                it.GSZ - it.NAV,
                it.NAV
            )
        }


    }.toList()
}

/**
 * 股票转换
 */
fun List<Diff>.diffToStockTransaction(): List<HoldingStockProfit.StockTransactionInfo> {
    return this.map {
        val nowPrice = if (it.f2.setScale(5) == BigDecimal.ZERO.setScale(5) || isNewBond(it)) {
            it.f18
        } else {
            it.f2
        }
        HoldingStockProfit.StockTransactionInfo(it.f12, it.f14, nowPrice, it.f3, it.f4, it.f18)
    }.toList()
}

// 两个日期是否同一天
fun LocalDateTime.isSameDay(other: LocalDateTime): Boolean {
    return this.toLocalDate().isSameDay(other.toLocalDate())

}

fun LocalDate.isSameDay(other: LocalDate):Boolean{
    return this == other
}

fun saveFile(content: String, fileName: String) {
    val path =
        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath + "/stockmarket"

    println(path)

    val file1 = File(path)
    if (!file1.exists()) {
        if (!file1.mkdirs()) {
            println("文件夹未能创建")
        }
    }


    val file = File(path, fileName)

    file.createNewFile()

    file.writeText(content)

}

fun readFile(fileName: String): String {
    val path =
        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath + "/stockmarket"
    val file = File(path, fileName)
    println(file.absoluteFile)
    if (file.exists() && file.isFile) {
        return file.readText()
    } else {
        throw FileNotFoundException("$fileName 不存在或者是文件夹")
    }
}

/**
 * 获取gson
 */
fun buildGson(): Gson = GsonBuilder()
    .registerTypeAdapter(LocalDateTime::class.java, LocalDateTimeAdapter())
    .registerTypeAdapter(LocalDate::class.java, LocalDateAdapter())
    .create()

/**
 * 给排序
 */
fun List<HoldingStockAdapter.Item>.sort(
    spinnerOrderType: Int,
    spinnerOrderItem: Int
): List<HoldingStockAdapter.Item> {
    return when (spinnerOrderType) {
        0 -> {
            this.sortedBy {
                sortBy(it, spinnerOrderItem)
            }
        }
        1 -> {
            this.sortedByDescending {
                sortBy(it, spinnerOrderItem)
            }
        }
        else -> {
            mutableListOf()
        }
    }
}

private fun sortBy(it: HoldingStockAdapter.Item, spinnerOrderItem: Int) =
    when (spinnerOrderItem) {
        0 -> it.holdingMoney
        1 -> it.allProfitPercent
        2 -> it.allProfit
        3 -> it.todayProfitPercent
        4 -> it.todayProfit
        5 -> it.holdingPrice // todo更新更新
        else -> it.todayProfit
    }

fun LocalDate.timeInterval(other: LocalDate): String {
    val period = Period.between(this, other)
    val buf = StringBuilder()
    if (period.years != 0) {
        buf.append(period.years).append(" 年 ")
    }
    if (period.months != 0) {
        buf.append(period.months).append(" 月 ")
    }
    if (period.days != 0) {
        buf.append(period.days).append(" 天")
    }
    return buf.toString()
}
fun String.getKlineCode(stockType: Stock.StockType,e:Stock.TransactionExchange): String {
    return if (stockType == Stock.StockType.FUND) {
        return this
    } else {
        "${e.getCode()}.${this}"
    }
}

fun Stock.TransactionExchange?.getKlineType(): String {
    return if (this == Stock.TransactionExchange.SH && this == Stock.TransactionExchange.SZ) {
        "hs"
    } else if (this == Stock.TransactionExchange.HK) {
        "hk"
    } else if (this == Stock.TransactionExchange.USA) {
        "us"
    } else {
        "hs"
    }
}

fun LocalDateTime.nonTradingHours():Boolean{


    return this.dayOfWeek.value > 5 ||  (this.hour in 1..8)
}

fun String.moneyFormat():String{
    val indexOf = this.indexOf('.')
    if (indexOf == -1) {
        return  this
    }

    val substring = this.substring(indexOf + 1)
    if (substring.length < 3) {
        return this
    }

    return this.substring(0,indexOf+1)+substring.substring(0,2)

}
/**
     * 对集合按照指定长度分段，每一个段为单独的集合，返回这个集合的列表
     *
     * @param <T>  集合元素类型
     * @param list 集合
     * @param size 每个段的长度
     * @return 分段列表
     */
 fun <T> List<T>.split(size: Int): List<List<T>> {
    val result = ArrayList<List<T>>()
    var remaider = this.size % size //(先计算出余数)
    val number = this.size / size  //然后是商
    var offset = 0//偏移量
    for (i in 0 until size) {
        var value: List<T>?
        if (remaider > 0) {
            value = this.subList(i * number + offset, (i + 1) * number + offset + 1)
            remaider--
            offset++
        } else {
            value = this.subList(i * number + offset, (i + 1) * number + offset)
        }
        result.add(value)
    }
    return result
}
